package zjut.raymond.witdemo.algorithm;


// 三维向量
public class Vector3 {
    public double x;
    public double y;
    public double z;

    // (构造) 以给定的x,y,z值生成新向量
    public Vector3(double x, double y, double z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    // (构造) 从数组构造
    public Vector3(float[] floatArray) throws ArrayIndexOutOfBoundsException {
        this.x = floatArray[0];
        this.y = floatArray[1];
        this.z = floatArray[2];
    }

    // (构造) 新空向量
    public Vector3() {
    }

    // (构造) 从已有向量生成向量
    public Vector3(Vector3 vector3) {
        this.x = vector3.x;
        this.y = vector3.y;
        this.z = vector3.z;
    }

    // 向量加向量
    public Vector3 add(Vector3 vector3) {
        x += vector3.x;
        y += vector3.y;
        z += vector3.z;
        return this;
    }

    // 向量位移
    public Vector3 add(double offsetX, double offsetY, double offsetZ) {
        x += offsetX;
        y += offsetY;
        z += offsetZ;
        return this;
    }

    // 数量积（点积)
    public Vector3 dotProduct(double d) {
        x *= d;
        y *= d;
        z *= d;
        return this;
    }

    // 向量取反
    public Vector3 reverse() {
        return dotProduct(-1);
    }

    // 置零
    public Vector3 clear() {
        x = y = z = 0;
        return this;
    }

    // --------------------------------
    // 以上方法为改变并返回自身的方法，不产生新向量
    // 以下方法会返回新的值或新向量，不改变原向量（以get修饰）
    // --------------------------------

    // 内积
    public double getDotProduct(Vector3 vector3) {
        return this.x * vector3.x + this.y * vector3.y + this.z * vector3.z;
    }

    // 向量的模
    public double getNorm() {
        return sqrtPowSum(x, y, z);
    }

    // 得到在给定向量上的投影值(负值代表反向)
    public double getProjection(Vector3 vector3) {
        return cos(this, vector3) * getNorm();
    }

    // 获得在给定向量上的投影向量
    public Vector3 getProjectVector(Vector3 vector3) {
        return vector3.getUnitVector().dotProduct(getProjection(vector3));
    }

    // 获得与给定向量投影垂直的向量
    public Vector3 getPerpendicular(Vector3 vector3) {
        return new Vector3(this).add(getProjectVector(vector3).reverse());
    }

    // 得到单位向量
    public Vector3 getUnitVector() {
        return new Vector3(this).dotProduct(1 / getNorm());
    }

    // 返回向量字符串
    public String toString() {
        return "Vector3(" + x + "," + y + "," + z + ")";
    }

    // 转为Float数组
    public float[] toFloatArray() {
        return new float[]{(float) this.x, (float) this.y, (float) this.z};
    }

    public Vector3 getCrossProduct(Vector3 vector, Boolean isRightHand) {
        return getCrossProduct(this, vector, isRightHand);
    }

    // --------------------------------
    // 以下方法为静态公共方法，与本向量无关
    // --------------------------------

    // (公共) 内积
    public static double getDotProduct(Vector3 vector3a, Vector3 vector3b) {
        return vector3a.x * vector3b.x + vector3a.y * vector3b.y + vector3a.z * vector3b.z;
    }

    // (公共)夹角余弦
    public static double cos(Vector3 vector3a, Vector3 vector3b) {
        return getDotProduct(vector3a, vector3b) / (norm(vector3a) * norm(vector3b));
    }

    // (公共)向量的模
    public static double norm(Vector3 vector3) {
        return sqrtPowSum(vector3.x, vector3.y, vector3.z);
    }

    // (公共)向量叉积/外积/向量积(右手系)
    public static Vector3 getCrossProduct(Vector3 a, Vector3 b, Boolean isRightHand) {
        double x = a.y * b.z - a.z * b.y;
        double y = a.z * b.x - a.x * b.z;
        double z = a.x * b.y - a.y * b.x;
        if (isRightHand) return new Vector3(x, y, z).reverse();
        else return new Vector3(x, y, z);
    }

    public static Vector3 getOuterProduct(Vector3 a, Vector3 b, Boolean isRightHand) {
        return getCrossProduct(a, b, isRightHand);
    }

    public static Vector3 getVectorProduct(Vector3 a, Vector3 b, Boolean isRightHand) {
        return getCrossProduct(a, b, isRightHand);
    }

    // 根号好几个数的平方
    public static double sqrtPowSum(double... num) {
        double sum = 0;
        for (double n : num) sum += Math.pow(n, 2);
        return Math.sqrt(sum);
    }
}

